// SPDX-License-Identifier: GPL-2.0-or-later
/**
 * @brief
 */
#ifndef FQTERM_TELNET_H
#define FQTERM_TELNET_H

#include <QAbstractSocket>
#include <QByteArray>
#include "fqterm_socket.h"
#include "connect_info.h"

namespace FQTerm {

#ifndef u_char
#   define u_char uchar
#endif

#define NTSTATES	6	    // # of Telnet Socket-Input FSM States
#define NSSTATES	3		// # of Telnet Option Subnegotiation FSM States
#define NCHRS		256		// # of valid characters

//  declaration
class FQTermTelnet;
// actionFunc is a pointer, point to a FQTermTelnet's func
typedef int(FQTermTelnet:: *ptrActionFunc)(int c);

//fsm struct
struct fsm_trans {
	u_char          ft_state;   // current state
	short           ft_char;    // input character
	u_char          ft_next;    // next state
	ptrActionFunc   ft_action;  // action to take
};


/*------------------------------------------------------------------------------
 *	FQTermTelnet class definition
 *-------------------------------------------------------------------------------
 */

// Telnet connection, a wrapper of socket.
// It will translate raw NVT data from low level socket to ansi data,
// and then upper level application can read it.
// It also can send ASCII data (0~127).
class FQTermTelnet: public QObject {
	Q_OBJECT
      public:
	FQTermTelnet(const QString &termtype, int rows, int numColumns, int protocolType, int hostType,
		     const char *sshuser = nullptr, const char *sshpasswd = nullptr );
	~FQTermTelnet();

	void setProxy(int nProxyType,  //0-no proxy; 1-wingate; 2-sock4; 3-socks5
		      bool bAuth,  // if authentation needed
		      const QString &strProxyHost, quint16 uProxyPort, const QString &strProxyUsr,
		      const QString &strProxyPwd);
	void connectHost(const QString &hostname, quint16 portnumber);

	// Read ANSI data.
	int read(char *data, uint maxlen);

	// Write data raw data
	int write(const char *data, uint len);

	void close(); // User close the connection

	int raw_len();
	int read_raw(char *data, uint maxlen);

	bool readyForInput();

	conn_info_t *connectionInfo() { return socket->connectionInfo(); }
	/**
	 * @brief 清理缓冲
	 */
	void clearBuffer(){
		from_socket.clear();
		raw_size = 0;
		socket->clear();
	}
	/**
	 * @brief 强制将状态清理成初始数据处理状态
	*/
	void clearState(){ ttstate = 0; }
      signals:
	void readyRead(int, int); // There are data to be read out
	void TelnetState(int); // The  state telnet, defined as TSXXXX in fqterm.h
	void requestUserPwd(QString *user, QString *pwd, bool *isOK);
	void warnInsecure(const QString&, bool *isOK);
	void errorMessage(QString);

      public slots:
	void windowSizeChanged(int, int);

      private slots:
	// Retrieve data from socket, translate NVT data to ansi data,
	// then notify data ready.
	void socketReadyRead();

	void connected();
	void showError(QAbstractSocket::SocketError);
	void hostFound();
	void delayCloseFinished();
	void closed();
      protected:
	//init structure fsm
	void fsmbuild();
	void fsminit(u_char fsm[][NCHRS], struct fsm_trans ttab[], int nstates);

	//actions
	int tcdm(int);
	int recopt(int);
	int no_op(int);
	int do_echo(int);
	int do_notsup(int);
	int do_noga(int);
	int do_txbinary(int);
	int will_notsup(int);
	int will_txbinary(int);
	int will_termtype(int);
	int will_naws(int);
	int subopt(int);
	int subtermtype(int);
	int subend(int);
	int soputc(int);
	int ttputc(int);
	int tnabort(int);

	//utility functions
	int xputc_up(char);
	int xputs_up(char*);
	void putc_down(u_char);

      signals:

	void onSSHAuthOK();

      private:
	bool              bConnected;
	char              noga;
	char              naws;
	char              server_sent_do_naws;
		// Boolean Flags
	char              synching, doecho, sndbinary, rcvbinary;

	u_char            option_cmd; // has value WILL, WONT, DO, or DONT

	char              termtype; // non-zero if received "DO TERMTYPE"
	char            * term; // terminal name
	// socket stuffs
	FQTermSocket    * socket;

	uint              rsize;         // size of to_ansi buffer
	uint              wsize;         // size of to_socket buffer


	int               wx, wy;         // for test
	int               done_naws;

	int               raw_size;
	int               hostType_;
	int               protocolType_;
	int               substate;       // 当前状态
	int               ttstate;        // 当前状态

	// FSM stuffs
	static struct fsm_trans ttstab[];
	u_char ttfsm[ NTSTATES ][ NCHRS ];
	static struct fsm_trans substab[];
	u_char subfsm[ NSSTATES ][ NCHRS ];

	//Pointers to internal buffers
	//
	//             |-->from_socket-->process-->to_ansi-->|
	//  socket<--->                                      <---> ansi decode
	//             |<---to_socket<--process<--from_ansi--|
	//
	QByteArray from_socket, to_ansi, from_ansi, to_socket;

};

}  // namespace FQTerm

#endif  // FQTERM_TELNET_H
